# -*- coding: utf-8 -*-
"""
车辆系统检测模块

提供车辆系统各项检测功能，包括系统版本、内核版本、容器镜像、
激光雷达数据、相机点云和RGB数据等检测功能。
"""

import re
import time
import os
import sys
from typing import Tuple, Dict, Any
from gglobal import logger, config


def check_system_version(ssh_client) -> Tuple[bool, str]:
    """
    检测系统版本

    Args:
        ssh_client: SSH客户端实例

    Returns:
        tuple: (检测成功状态, 系统版本字符串)
    """
    try:
        logger.info("开始检测系统版本...")
        result = ssh_client.channel_send_cmd("lsb_release -a", timeout=5)

        # 解析系统版本信息
        lines = result.split("\n")
        for line in lines:
            if "Description:" in line:
                # 提取版本信息，去除前缀
                version = line.split("Description:")[-1].strip()
                logger.info(f"系统版本检测成功: {version}")
                return True, version

        # 如果没有找到Description字段，尝试其他方式
        for line in lines:
            if "Release:" in line:
                release = line.split("Release:")[-1].strip()
                for dist_line in lines:
                    if "Distributor ID:" in dist_line:
                        dist_id = dist_line.split("Distributor ID:")[-1].strip()
                        version = f"{dist_id} {release}"
                        logger.info(f"系统版本检测成功: {version}")
                        return True, version

        logger.warning("无法解析系统版本信息")
        return False, "未知系统版本"

    except Exception as e:
        error_msg = f"系统版本检测失败: {str(e)}"
        logger.error(error_msg)
        return False, error_msg


def check_kernel_version(ssh_client) -> Tuple[bool, str]:
    """
    检测内核版本

    Args:
        ssh_client: SSH客户端实例

    Returns:
        tuple: (检测成功状态, 内核版本字符串)
    """
    try:
        logger.info("开始检测内核版本...")
        result = ssh_client.channel_send_cmd("uname -a", timeout=5)
        logger.info(f"uname -a 输出: {result}")
        
        # 清理输出，移除命令回显和提示符
        lines = result.strip().split('\n')
        uname_output = None
        
        # 查找包含内核信息的行（通常以"Linux"开头）
        for line in lines:
            line = line.strip()
            if line.startswith('Linux') and not line.startswith('Linux '):
                # 跳过可能的命令行
                continue
            elif line.startswith('Linux ') and len(line.split()) >= 6:
                uname_output = line
                break
        
        if not uname_output:
            logger.warning("无法找到有效的uname输出")
            return False, "未知内核版本"
        
        # 解析内核版本信息
        parts = uname_output.split()
        if len(parts) >= 3:
            # parts[0] = "Linux"
            # parts[1] = hostname
            # parts[2] = kernel version
            kernel_version = parts[2]
            
            # 查找架构信息（通常是x86_64等）
            architecture = None
            for part in parts:
                if part in ['x86_64', 'aarch64', 'armv7l', 'i386', 'i686']:
                    architecture = part
                    break
            
            if architecture:
                version_info = f"{kernel_version} ({architecture})"
            else:
                version_info = kernel_version
                
            logger.info(f"内核版本检测成功: {version_info}")
            return True, version_info
        else:
            logger.warning("无法解析内核版本信息")
            return False, "未知内核版本"

    except Exception as e:
        error_msg = f"内核版本检测失败: {str(e)}"
        logger.error(error_msg)
        return False, error_msg


def check_docker_images(ssh_client) -> Tuple[bool, str]:
    """
    检测容器镜像版本

    Args:
        ssh_client: SSH客户端实例

    Returns:
        tuple: (检测成功状态, 容器镜像信息)
    """
    try:
        logger.info("开始检测容器镜像...")
        result = ssh_client.channel_send_cmd('docker ps --format "{{.Image}}"', timeout=5)
        logger.info(f"docker ps --format \"{{.Image}}\" 输出: {result}")

        # 清理输出，移除命令回显和提示符
        lines = result.strip().split('\n')
        image_lines = []
        
        # 过滤出有效的镜像行
        for line in lines:
            line = line.strip()
            # 跳过命令行、提示符和空行
            if (line and 
                not line.startswith('docker ps') and 
                not line.endswith('$') and 
                'harbor.edge.mooe-robot.com' in line):
                image_lines.append(line)

        if not image_lines:
            logger.warning("未检测到运行中的容器")
            return False, "无运行中的容器"

        # 优先查找focal-full镜像
        focal_full_image = None
        mooe_core_image = None
        
        for line in image_lines:
            if "mooe-core-focal-full" in line:
                focal_full_image = line
                break
            elif "mooe-core" in line and not mooe_core_image:
                mooe_core_image = line

        # 优先返回focal-full镜像
        if focal_full_image:
            logger.info(f"检测到mooe-core-focal-full镜像: {focal_full_image}")
            return True, focal_full_image
        elif mooe_core_image:
            logger.info(f"检测到mooe-core镜像: {mooe_core_image}")
            return True, mooe_core_image
        else:
            # 返回第一个镜像作为默认结果
            first_image = image_lines[0]
            logger.info(f"未找到mooe-core镜像，返回第一个镜像: {first_image}")
            return True, first_image

    except Exception as e:
        error_msg = f"容器镜像检测失败: {str(e)}"
        logger.error(error_msg)
        return False, error_msg


def check_rostopic_hz(ssh_client, topic_name: str, timeout: int = 5) -> bool:
    """
    检测ROS话题频率

    Args:
        ssh_client: SSH客户端实例
        topic_name: 话题名称
        timeout: 检测超时时间

    Returns:
        bool: 话题是否正常（有数据发布）
    """
    try:
        logger.info(f"开始检测话题: {topic_name}")

        # 使用rostopic hz检测话题频率，设置较短的超时时间
        cmd = f"timeout {timeout} rostopic hz {topic_name}"
        result = ssh_client.channel_send_cmd(cmd, timeout=timeout + 2)

        # 检查结果中是否包含频率信息
        if "average rate:" in result.lower() or "hz" in result.lower():
            logger.info(f"话题 {topic_name} 检测成功，有数据发布")
            return True
        elif "no messages received" in result.lower() or "timeout" in result.lower():
            logger.warning(f"话题 {topic_name} 无数据发布")
            return False
        else:
            # 如果没有明确的错误信息，认为话题存在
            logger.info(f"话题 {topic_name} 状态未知，假设正常")
            return True

    except Exception as e:
        logger.error(f"话题 {topic_name} 检测失败: {str(e)}")
        return False


def check_lidar_data(ssh_client) -> Dict[str, bool]:
    """
    检测激光雷达数据

    Args:
        ssh_client: SSH客户端实例

    Returns:
        dict: 各激光雷达检测结果
    """
    logger.info("开始检测激光雷达数据...")

    results = {}

    # 上激光检测
    results["rslidar_points"] = check_rostopic_hz(ssh_client, "/rslidar_points")

    # 下激光检测（单下激光）
    results["scan_init_down"] = check_rostopic_hz(ssh_client, "/scan_init_down")

    # 下激光检测（双下激光）
    results["scan_down_left"] = check_rostopic_hz(ssh_client, "/scan_down_left")
    results["scan_down_right"] = check_rostopic_hz(ssh_client, "/scan_down_right")

    # 判断下激光整体状态：单下激光或双下激光有一个正常即认为下激光正常
    down_lidar_ok = results["scan_init_down"] or results["scan_down_left"] or results["scan_down_right"]

    logger.info(f"激光雷达检测结果: 上激光={results['rslidar_points']}, " f"下激光整体={down_lidar_ok}")

    return results


def check_mid360_lidar(ssh_client) -> bool:
    """
    检测Mid360激光雷达

    Args:
        ssh_client: SSH客户端实例

    Returns:
        bool: Mid360激光雷达是否正常
    """
    logger.info("开始检测Mid360激光雷达...")
    return check_rostopic_hz(ssh_client, "/livox/lidar")


def check_camera_depth_points(ssh_client) -> Dict[str, bool]:
    """
    检测相机点云数据

    Args:
        ssh_client: SSH客户端实例

    Returns:
        dict: 各相机点云检测结果
    """
    logger.info("开始检测相机点云数据...")

    results = {}

    # 左叉臂相机点云
    results["camera1_depth_points"] = check_rostopic_hz(ssh_client, "/camera1/depth/color/points")

    # 右叉臂相机点云
    results["camera2_depth_points"] = check_rostopic_hz(ssh_client, "/camera2/depth/color/points")

    logger.info(f"相机点云检测结果: 左叉臂={results['camera1_depth_points']}, " f"右叉臂={results['camera2_depth_points']}")

    return results


def check_camera_rgb_data(ssh_client) -> Dict[str, bool]:
    """
    检测相机RGB数据

    Args:
        ssh_client: SSH客户端实例

    Returns:
        dict: 各相机RGB检测结果
    """
    logger.info("开始检测相机RGB数据...")

    results = {}

    # 左叉臂相机RGB
    results["camera1_color_image_raw"] = check_rostopic_hz(ssh_client, "/camera1/color/image_raw")

    # 右叉臂相机RGB
    results["camera2_color_image_raw"] = check_rostopic_hz(ssh_client, "/camera2/color/image_raw")

    logger.info(f"相机RGB检测结果: 左叉臂={results['camera1_color_image_raw']}, " f"右叉臂={results['camera2_color_image_raw']}")

    return results

def ensure_calibration_script_in_container(ssh_client) -> Dict[str, Any]:
    """
    标定脚本检测与部署（容器内）
    使用固定远程目录 `/mooe/log`，本地路径基于 `main.py` 执行目录。

    返回:
        dict: 部署状态字典
    """
    # 状态字典，统一记录检查与部署过程
    status: Dict[str, Any] = {
        "checked": True,
        "exists_before": False,
    }
    try:
        # 容器名与目标脚本路径
        container_name = "mooe-core"
        target_script_path = "/home/muyi/mooe-core/share/Sensor_Calibration/launch/Sensor_Calibration.sh"

        # 先检查容器内是否已存在标定脚本
        check_cmd = (
            f"docker exec {container_name} bash -lc "
            f"'if [ -f {target_script_path} ]; then echo FOUND; else echo NOT_FOUND; fi'"
        )
        check_output = ssh_client.channel_send_cmd(check_cmd, timeout=10)
        exists_before = "FOUND" in check_output

        # 记录初始检查结果
        status["check_output"] = check_output
        status["exists_before"] = exists_before

        if exists_before:
            # 已存在则无需部署
            logger.info("标定文件已存在，跳过部署")
            status["deploy_skipped"] = True
            status["deploy_success"] = True
            return status

        # 固定远程日志路径，不从配置读取
        remote_log_dir = "/mooe/log"
        remote_tar_path = remote_log_dir.rstrip("/") + "/Calibration_Script.tar.gz"

        # 使用main.py既程序执行目录的相对路径来定位本地压缩包
        base_dir = os.path.dirname(os.path.abspath(sys.argv[0]))
        local_tar_path = os.path.join(base_dir, "resource", "script", "Calibration_Script.tar.gz")

        # 检查本地压缩包是否存在
        if not os.path.isfile(local_tar_path):
            err = f"本地压缩包不存在: {local_tar_path}"
            logger.error(err)
            status["deploy_success"] = False
            status["error"] = err
            return status

        # 通过SFTP上传到远程 /mooe/log
        upload_detail: Dict[str, Any] = {}
        upload_success = False
        try:
            if hasattr(ssh_client, "sftp_put_advanced"):
                upload_detail = ssh_client.sftp_put_advanced(
                    localfile=local_tar_path,
                    remotefile=remote_tar_path,
                    chunk_size=65536,
                    resume=True,
                    progress_callback=None,
                )
                upload_success = bool(upload_detail.get("success", False))
            elif hasattr(ssh_client, "sftp_put"):
                upload_detail = ssh_client.sftp_put(local_tar_path, remote_tar_path)
                # 旧接口不一定返回结构化结果，视为成功以继续后续步骤
                upload_success = True
            else:
                upload_detail = {"success": False, "message": "SSH客户端不支持SFTP上传接口"}
                upload_success = False
        except Exception as e:
            upload_detail = {"success": False, "message": str(e)}
            upload_success = False

        status["upload_detail"] = upload_detail
        if not upload_success:
            msg = f"SFTP上传失败: {upload_detail}"
            logger.error(msg)
            status["deploy_success"] = False
            status["deploy_error"] = msg
            return status

        # 在容器内解压并复制到目标目录
        try:
            # 解压压缩包
            decompress_cmd = (
                f"docker exec {container_name} bash -lc "
                f"'cd {remote_log_dir} && tar -xzf Calibration_Script.tar.gz'"
            )
            # 复制到mooe-core目录并确保脚本可执行
            copy_cmd = (
                f"docker exec {container_name} bash -lc "
                f"'cp -rf {remote_log_dir}/Calibration_Script/share/Sensor_Calibration /home/muyi/mooe-core/share/ && "
                f"chmod +x {target_script_path}'"
            )

            decompress_output = ssh_client.channel_send_cmd(decompress_cmd, timeout=60)
            copy_output = ssh_client.channel_send_cmd(copy_cmd, timeout=30)

            status["decompress_output"] = decompress_output
            status["copy_output"] = copy_output
        except Exception as e:
            msg = f"容器内解压或复制失败: {str(e)}"
            logger.error(msg)
            status["deploy_success"] = False
            status["deploy_error"] = msg
            return status

        # 部署完成后再次验证文件是否存在
        try:
            verify_output = ssh_client.channel_send_cmd(check_cmd, timeout=10)
            exists_after = "FOUND" in verify_output
            status["exists_after_deploy"] = exists_after
            status["verify_output"] = verify_output
            status["deploy_success"] = exists_after
            if exists_after:
                logger.info("部署完成并验证成功，标定文件已存在")
            else:
                logger.error("部署完成但验证失败，标定文件仍不存在")
        except Exception as e:
            msg = f"部署后验证失败: {str(e)}"
            logger.error(msg)
            status["deploy_success"] = False
            status["verify_error"] = msg

        return status

    except Exception as e:
        error_msg = f"标定脚本检测与部署发生异常: {str(e)}"
        logger.error(error_msg)
        status["deploy_success"] = False
        status["error"] = error_msg
        return status

def perform_full_system_check(ssh_client) -> Dict[str, Any]:
    """
    执行完整的系统检测

    Args:
        ssh_client: SSH客户端实例

    Returns:
        dict: 完整的检测结果
    """
    logger.info("开始执行完整的车辆系统检测...")

    results = {}

    try:
        # 系统版本检测
        success, system_version = check_system_version(ssh_client)
        results["system_version"] = system_version if success else "检测失败"

        # 内核版本检测
        success, kernel_version = check_kernel_version(ssh_client)
        results["kernel_version"] = kernel_version if success else "检测失败"

        # 容器镜像检测
        success, docker_image = check_docker_images(ssh_client)
        results["mooe-core"] = docker_image if success else "检测失败"

        # 激光雷达数据检测
        lidar_results = check_lidar_data(ssh_client)
        results.update(lidar_results)

        # Mid360激光检测
        results["livox_lidar"] = check_mid360_lidar(ssh_client)

        # 相机点云检测
        camera_depth_results = check_camera_depth_points(ssh_client)
        results.update(camera_depth_results)

        # 相机RGB检测
        camera_rgb_results = check_camera_rgb_data(ssh_client)
        results.update(camera_rgb_results)

        # 标定文件检测与部署逻辑重构：封装为独立函数并调用
        status = ensure_calibration_script_in_container(ssh_client)
        results["calibration_script_status"] = status

        # 重构后直接返回，避免执行旧的部署逻辑块
        logger.info("车辆系统检测完成")
        return results

    except Exception as e:
        error_msg = f"系统检测过程中发生异常: {str(e)}"
        logger.error(error_msg)
        results["error"] = error_msg
        return results


def read_and_validate_calibration_params() -> Dict[str, Any]:
    """
    从参数管理器读取车辆校准检测参数并进行验证

    Returns:
        dict: 校准参数字典，包含验证状态
    """
    try:
        logger.info("开始读取车辆校准检测参数...")

        # 从参数管理器读取 car_calibration_check 参数
        calibration_params = config.get_param("car_calibration_check", {})

        if not calibration_params:
            logger.warning("未找到车辆校准检测参数，使用默认值")
            calibration_params = {
                "system_version": "",
                "kernel_version": "",
                "mooe-core": "",
                "rslidar_points": False,
                "scan_init_down": False,
                "scan_down_left": False,
                "scan_down_right": False,
                "livox_lidar": False,
                "camera1_depth_points": False,
                "camera2_depth_points": False,
                "camera1_color_image_raw": False,
                "camera2_color_image_raw": False,
            }
        # 参数有效性验证
        validation_results = {
            "system_version_valid": bool(calibration_params.get("system_version", "")),
            "kernel_version_valid": bool(calibration_params.get("kernel_version", "")),
            "rslidar_points_valid": isinstance(calibration_params.get("rslidar_points", 0), (int, float)) and calibration_params.get("rslidar_points", 0) >= 0,
        }

        logger.info("参数读取完成")

        return {"params": calibration_params, "validation": validation_results, "success": True}

    except Exception as e:
        error_msg = f"读取校准参数时发生异常: {str(e)}"
        logger.error(error_msg)

        return {"params": {}, "validation": {}, "success": False, "error": error_msg}


def write_calibration_check_results(results: Dict[str, Any]) -> Dict[str, Any]:
    """
    将车辆校准检测结果写入参数管理器
    
    Args:
        results: 检测结果字典
        
    Returns:
        dict: 写入操作结果
    """
    try:
        logger.info("开始写入车辆校准检测结果...")
        
        # 验证输入参数
        if not isinstance(results, dict):
            raise ValueError("检测结果必须是字典类型")
        
        # 构建要写入的校准检测结果
        calibration_results = {
            'system_version': results.get('system_version', ''),
            'kernel_version': results.get('kernel_version', ''),
            'docker_images': results.get('docker_images', {}),
            'rslidar_points': results.get('rslidar_points', False),
            'camera_depth_points': results.get('camera_depth_points', {}),
            'camera_rgb_points': results.get('camera_rgb_points', {}),
            'last_check_time': time.time(),
            'check_status': 'completed'
        }
        
        # 写入参数管理器
        config.set_param('car_calibration_check', calibration_results)
        
        logger.info("校准检测结果写入完成")
        
        return {
            'success': True,
            'written_params': calibration_results
        }
        
    except Exception as e:
        error_msg = f"写入校准检测结果时发生异常: {str(e)}"
        logger.error(error_msg)
        
        return {
            'success': False,
            'error': error_msg
        }


def reset_calibration_check_params() -> Dict[str, Any]:
    """
    重置车辆校准检测参数为默认值
    
    该函数将car_calibration_check参数重置为系统初始默认值，
    确保每次检测前都有一个干净的参数状态。
    
    Returns:
        dict: 重置操作结果，包含成功状态和详细信息
    """
    try:
        logger.info("开始重置车辆校准检测参数...")
        
        # 定义默认参数值（与param_manager.py中的默认值保持一致）
        default_calibration_params = {
            "system_version": "",
            "kernel_version": "",
            "mooe-core": "",
            "rslidar_points": False,
            "scan_init_down": False,
            "scan_down_left": False,
            "scan_down_right": False,
            "livox_lidar": False,
            "camera1_depth_points": False,
            "camera2_depth_points": False,
            "camera1_color_image_raw": False,
            "camera2_color_image_raw": False
        }
        
        # 验证默认参数的有效性
        validation_result = _validate_calibration_params(default_calibration_params)
        if not validation_result["is_valid"]:
            error_msg = f"默认参数验证失败: {validation_result['errors']}"
            logger.error(error_msg)
            return {
                "success": False,
                "error": error_msg,
                "reset_params": None
            }
        
        # 将默认参数写入配置管理器
        config.set_param('car_calibration_check', default_calibration_params)
        
        # 验证写入是否成功
        saved_params = config.get_param('car_calibration_check', {})
        if saved_params != default_calibration_params:
            error_msg = "参数重置后验证失败，写入的参数与预期不符"
            logger.error(error_msg)
            return {
                "success": False,
                "error": error_msg,
                "reset_params": saved_params
            }
        
        logger.info("车辆校准检测参数重置成功")
        logger.debug(f"重置后的参数: {default_calibration_params}")
        
        return {
            "success": True,
            "message": "参数重置成功",
            "reset_params": default_calibration_params
        }
        
    except Exception as e:
        error_msg = f"重置车辆校准检测参数时发生异常: {str(e)}"
        logger.error(error_msg)
        
        return {
            "success": False,
            "error": error_msg,
            "reset_params": None
        }


def _validate_calibration_params(params: Dict[str, Any]) -> Dict[str, Any]:
    """
    验证车辆校准检测参数的有效性
    
    Args:
        params: 待验证的参数字典
        
    Returns:
        dict: 验证结果，包含是否有效和错误信息
    """
    try:
        errors = []
        
        # 检查必需的字符串字段
        string_fields = ["system_version", "kernel_version", "mooe-core"]
        for field in string_fields:
            if field not in params:
                errors.append(f"缺少必需字段: {field}")
            elif not isinstance(params[field], str):
                errors.append(f"字段 {field} 必须是字符串类型")
        
        # 检查必需的布尔字段
        boolean_fields = [
            "rslidar_points", "scan_init_down", "scan_down_left", "scan_down_right",
            "livox_lidar", "camera1_depth_points", "camera2_depth_points",
            "camera1_color_image_raw", "camera2_color_image_raw"
        ]
        for field in boolean_fields:
            if field not in params:
                errors.append(f"缺少必需字段: {field}")
            elif not isinstance(params[field], bool):
                errors.append(f"字段 {field} 必须是布尔类型")
        
        # 检查是否有多余的字段
        expected_fields = set(string_fields + boolean_fields)
        actual_fields = set(params.keys())
        extra_fields = actual_fields - expected_fields
        if extra_fields:
            logger.warning(f"发现多余的参数字段: {extra_fields}")
        
        is_valid = len(errors) == 0
        
        return {
            "is_valid": is_valid,
            "errors": errors,
            "extra_fields": list(extra_fields)
        }
        
    except Exception as e:
        return {
            "is_valid": False,
            "errors": [f"参数验证过程中发生异常: {str(e)}"],
            "extra_fields": []
        }


def perform_calibration_check_with_params(ssh_client) -> Dict[str, Any]:
    """
    执行带参数管理的车辆校准检测

    Args:
        ssh_client: SSH客户端实例

    Returns:
        dict: 包含参数读取、检测和写入结果的完整信息
    """
    logger.info("开始执行带参数管理的车辆校准检测...")

    # 1. 读取和验证校准参数
    param_result = read_and_validate_calibration_params()

    # 2. 执行系统检测
    detection_results = perform_full_system_check(ssh_client)

    # 3. 写入检测结果
    write_result = write_calibration_check_results(detection_results)

    # 4. 返回完整结果
    return {
        "param_read_result": param_result,
        "detection_results": detection_results,
        "write_result": write_result,
        "overall_success": param_result["success"] and write_result["success"] and not detection_results.get("error"),
    }
